#include "Logger.h"
#include <chrono>
#include <iomanip>
#include <sstream>
#include <cstring>
#include <unordered_map>
#include "include/ConfigJson.h"
#include "include/BlockQueue.h"
#include "include/MemoryPool.h"
#include <shared_mutex>

namespace Tool
{

const std::vector<std::string> levelString = {
    "debug",
    "info",
    "warning",
    "error"
};

const std::string RESET = "\033[0m";
const std::string RED = "\033[31m";
const std::string YELLOW = "\033[33m";
const size_t BUF_SIZE = 8192;
const size_t HEADER_SIZE = 5; // 4字节长度 + 1字节级别
const size_t CONTENT_MAX_SIZE = BUF_SIZE - HEADER_SIZE; // 最大可写内容长度：8187字节

using LogMemPool = MemoryPool<char[BUF_SIZE], 2000>;

struct LoggerPrivater
{
    bool m_isInitialized = false;
    std::string m_logPath = "./logs";
    std::string m_logFileName = ".log";
    int m_logBufSize = 2048;
    int m_splitLines = 500000;
    std::atomic<int> m_countLine = 0;
    LogLevel m_logLevel = LogLevel::Debug;
    bool m_outputToConsole = true;
    std::unique_ptr<BlockQueue<char *>> m_logQueue;
    int m_asyncLogSize = 0;
    std::ofstream m_logFile;
    std::thread m_asyncThread;
    std::atomic<bool> m_isRunning = false;
    std::mutex m_mutex;
    std::unique_ptr<LogMemPool> m_memoryPool; // 内存池
};

Logger::Logger()
    :m_privater(new LoggerPrivater)
{
    try
    {
        ConfigJson configIni;
        configIni.Load("config.json");
        std::string logPath = configIni.GetValue("Log", "logPath", "logs");
        std::string logAkhiranName = configIni.GetValue("Log", "logAkhiranName",".log");
        bool outputToConsole = configIni.GetValue("Log", "outputToConsole", true);
        int level = configIni.GetValue("Log", "level", 0);
        int asyncLogSize = configIni.GetValue("Log", "asyncLogSize", 20000);
        int logBufSize = configIni.GetValue("Log", "logBufSize", 8192);
        int splitLines = configIni.GetValue("Log", "splitLines", 500000);
        Init(logPath, logAkhiranName, outputToConsole, static_cast<LogLevel>(level), asyncLogSize, logBufSize, splitLines);
        configIni.Save("config.json");
    }
    catch (const std::bad_variant_access &e)
    {
        Init("logs", ".log");
    }
}

Logger::~Logger()
{
    UnInit();
    if(m_privater)
    {
        delete m_privater;
        m_privater = nullptr;
    }
}

bool Logger::Init(const std::string &logPath, const std::string &logFileName, bool outputToConsole, LogLevel level,
                  int asyncLogSize, int logBufSize, int splitLines)
{
    if(m_privater->m_isInitialized)
    {
        std::cerr << "It's already initialized" << std::endl;
        return false;
    }
    m_privater->m_logPath = logPath;
    m_privater->m_logFileName = logFileName;
    m_privater->m_logLevel = level;
    m_privater->m_logBufSize = logBufSize;
    m_privater->m_splitLines = splitLines;
    m_privater->m_asyncLogSize = asyncLogSize;
    m_privater->m_outputToConsole = outputToConsole;

    if (!std::filesystem::exists(m_privater->m_logPath))
    {
        if (!std::filesystem::create_directories(m_privater->m_logPath))
        {
            std::cerr << "Failed to create directory: " << m_privater->m_logPath << std::endl;
            return false;
        }
    }

    std::string logFile = GetLogFileName();
    m_privater->m_logFile.open(logFile, std::ios::app);
    if (!m_privater->m_logFile.is_open())
    {
        std::cerr << "Open log file failed" << std::endl;
        return false;
    }

    m_privater->m_isRunning = true;
    m_privater->m_logQueue = std::make_unique<BlockQueue<char *>>(m_privater->m_asyncLogSize);
    m_privater->m_asyncThread = std::thread(&Logger::AsyncWriteLog, this);
    m_privater->m_memoryPool = std::make_unique<LogMemPool>();
    m_privater->m_isInitialized = true;
    return true;
}

void Logger::UnInit()
{
    m_privater->m_isRunning = false;
    m_privater->m_logQueue->Stop();
    if (m_privater->m_asyncThread.joinable())
    {
        m_privater->m_asyncThread.join();
    }
    if (m_privater->m_logFile.is_open())
    {
        m_privater->m_logFile.close();
    }
    m_privater->m_isInitialized = false;
}

void Logger::Write(const char *fileName, int lineNumber, LogLevel level, std::string logerName, const char *format, ...)
{
    if (!m_privater->m_isInitialized || level < m_privater->m_logLevel)
    {
        return;
    }
    int level_index = static_cast<int>(level);
    auto now = std::chrono::system_clock::now();
    auto time = std::chrono::system_clock::to_time_t(now);
    std::tm tm = {};
#if defined(_WIN32) || defined(_WIN64)
    if (localtime_s(&tm, &time) != 0)
#else
    if (localtime_r(&time, &tm) == nullptr)
#endif
    {
        std::cerr << "Failed to convert time to local time." << std::endl;
    }
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
    std::string path = fileName;
    static std::unordered_map<std::string, std::string> fileNameCache;
    static std::shared_mutex cacheMutex; // 加读写锁
    {
        std::shared_lock<std::shared_mutex> readLock(cacheMutex); // 读用共享锁
        auto it = fileNameCache.find(fileName);
        if (it != fileNameCache.end())
        {
            path = it->second;
        }
        else
        {
            path = fileName;
            size_t last_pos = path.find_last_of("/\\");
            if (last_pos != std::string::npos)
            {
                path = path.substr(last_pos + 1);
            }
            last_pos = path.find_last_of(".");
            if (last_pos != std::string::npos)
            {
                path = path.substr(0, last_pos);
            }
            fileNameCache[fileName] = path;
        }
    }

    char (*logBuf)[8192] = m_privater->m_memoryPool->allocate();
    va_list args;
    va_start(args, format);
    char* content_start = *logBuf + HEADER_SIZE; //写入起始地址
    int content_len = std::snprintf(content_start, CONTENT_MAX_SIZE, "[%04d-%02d-%02d %02d:%02d:%02d.%03lld %s:%d] [%s] ",
                          tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
                          tm.tm_hour, tm.tm_min, tm.tm_sec, ms.count(),
                          path.c_str(), lineNumber, levelString.at(level_index).c_str());
    content_len += std::vsnprintf(content_start + content_len, CONTENT_MAX_SIZE - content_len, format, args);
    va_end(args);
    *reinterpret_cast<int*>(*logBuf) = content_len; // 把int长度直接存到0-3字节
    (*logBuf)[4] = static_cast<char>(level);
    m_privater->m_logQueue->Push(*logBuf);
}

void Logger::AsyncWriteLog()
{
    while (m_privater->m_isRunning || !m_privater->m_logQueue->IsEmpty())
    {
        const char* logMsg = m_privater->m_logQueue->Pop();
        if (m_privater->m_countLine >= m_privater->m_splitLines)
        {
            m_privater->m_logFile.flush();
            m_privater->m_logFile.close();
            m_privater->m_logFile.open(GetLogFileName(), std::ios::app);
            m_privater->m_countLine = 0;
        }
        int content_len = *reinterpret_cast<const int*>(logMsg);
        const char* content_start = logMsg + HEADER_SIZE;

//        m_privater->m_logFile << logMsg << '\n';
        m_privater->m_logFile.write(content_start, content_len); // 写前8191字节内容
        m_privater->m_logFile.put('\n'); // 单独加换行符

        m_privater->m_countLine++;
        if (m_privater->m_outputToConsole)
        {
#ifdef __linux__
            LogLevel level = static_cast<LogLevel>(logMsg[4]);
            if (level == LogLevel::Warning)
            {
                std::cout << YELLOW;
                std::cout.write(content_start, content_len);
                std::cout << RESET << '\n';
            }
            else if (level == LogLevel::Error)
            {
                std::cout << RED;
                std::cout.write(content_start, content_len);
                std::cout << RESET << '\n';
            }
            else
            {
                std::cout.write(content_start, content_len);
                std::cout.put('\n');
            }
#else
            std::cout.write(content_start, content_len);
            std::cout.put('\n');
#endif
        }
        m_privater->m_memoryPool->deallocate(reinterpret_cast<char(*)[BUF_SIZE]>(const_cast<char*>(logMsg)));
    }
}

std::string Logger::GetLogFileName()
{
    auto now = std::chrono::system_clock::now();
    auto time = std::chrono::system_clock::to_time_t(now);
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
    std::tm tm = {};
#if defined(_WIN32) || defined(_WIN64)
    localtime_s(&tm, &time);
#else
    localtime_r(&time, &tm);
#endif
    std::ostringstream oss;
//    oss << m_privater->m_logPath << "/" << std::put_time(&tm, "%Y%m%dT%H%M%S") << m_privater->m_logFileName;
    oss << m_privater->m_logPath << "/" << std::put_time(&tm, "%Y%m%dT%H%M%S") << std::setw(3) << std::setfill('0') << ms.count() << m_privater->m_logFileName;
    return oss.str();
}

void Logger::SetOutputToConsole(bool output)
{
    std::unique_lock<std::mutex> lock(m_privater->m_mutex);
    m_privater->m_outputToConsole = output;
}

void Logger::SetLevel(LogLevel level)
{
    std::unique_lock<std::mutex> lock(m_privater->m_mutex);
    m_privater->m_logLevel = level;
}

}
